#region Apache License
// .NET Compact Framework 1.0 has no support for System.Runtime.Remoting
//
// Licensed to the Apache Software Foundation (ASF) under one or more 
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership. 
// The ASF licenses this file to you under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with 
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#endregion

// .NET Compact Framework 1.0 && netstandard has no support for System.Runtime.Remoting
#if NET_2_0

using System;
using System.Runtime.Remoting;

using log4net.Util;
using log4net.Repository;
using log4net.Core;
using IRemoteLoggingSink = log4net.Appender.RemotingAppender.IRemoteLoggingSink;

namespace log4net.Plugin
{
  /// <summary>
  /// Plugin that listens for events from the <see cref="log4net.Appender.RemotingAppender"/>
  /// </summary>
  /// <remarks>
  /// <para>
  /// This plugin publishes an instance of <see cref="IRemoteLoggingSink"/> 
  /// on a specified <see cref="SinkUri"/>. This listens for logging events delivered from
  /// a remote <see cref="log4net.Appender.RemotingAppender"/>.
  /// </para>
  /// <para>
  /// When an event is received it is relogged within the attached repository
  /// as if it had been raised locally.
  /// </para>
  /// </remarks>
  /// <author>Nicko Cadell</author>
  /// <author>Gert Driesen</author>
  public class RemoteLoggingServerPlugin : PluginSkeleton
  {
    #region Public Instance Constructors

    /// <summary>
    /// Default constructor
    /// </summary>
    /// <remarks>
    /// <para>
    /// Initializes a new instance of the <see cref="RemoteLoggingServerPlugin" /> class.
    /// </para>
    /// <para>
    /// The <see cref="SinkUri"/> property must be set.
    /// </para>
    /// </remarks>
    public RemoteLoggingServerPlugin() : base("RemoteLoggingServerPlugin:Unset URI")
    {
    }

    /// <summary>
    /// Construct with sink Uri.
    /// </summary>
    /// <param name="sinkUri">The name to publish the sink under in the remoting infrastructure. 
    /// See <see cref="SinkUri"/> for more details.</param>
    /// <remarks>
    /// <para>
    /// Initializes a new instance of the <see cref="RemoteLoggingServerPlugin" /> class
    /// with specified name.
    /// </para>
    /// </remarks>
    public RemoteLoggingServerPlugin(string sinkUri) : base("RemoteLoggingServerPlugin:" + sinkUri)
    {
      m_sinkUri = sinkUri;
    }

    #endregion Public Instance Constructors

    #region Public Instance Properties

    /// <summary>
    /// Gets or sets the URI of this sink.
    /// </summary>
    /// <value>
    /// The URI of this sink.
    /// </value>
    /// <remarks>
    /// <para>
    /// This is the name under which the object is marshaled.
    /// <see cref="M:RemotingServices.Marshal(MarshalByRefObject,String,Type)"/>
    /// </para>
    /// </remarks>
    public virtual string SinkUri
    {
      get { return m_sinkUri; }
      set { m_sinkUri = value; }
    }

    #endregion Public Instance Properties

    #region Override implementation of PluginSkeleton

    /// <summary>
    /// Attaches this plugin to a <see cref="ILoggerRepository"/>.
    /// </summary>
    /// <param name="repository">The <see cref="ILoggerRepository"/> that this plugin should be attached to.</param>
    /// <remarks>
    /// <para>
    /// A plugin may only be attached to a single repository.
    /// </para>
    /// <para>
    /// This method is called when the plugin is attached to the repository.
    /// </para>
    /// </remarks>
#if NET_4_0 || MONO_4_0
    [System.Security.SecuritySafeCritical]
#endif
    public override void Attach(ILoggerRepository repository)
    {
      base.Attach(repository);

      // Create the sink and marshal it
      m_sink = new RemoteLoggingSinkImpl(repository);

      try
      {
        RemotingServices.Marshal(m_sink, m_sinkUri, typeof(IRemoteLoggingSink));
      }
      catch (Exception ex)
      {
        LogLog.Error(declaringType, "Failed to Marshal remoting sink", ex);
      }
    }

    /// <summary>
    /// Is called when the plugin is to shutdown.
    /// </summary>
    /// <remarks>
    /// <para>
    /// When the plugin is shutdown the remote logging
    /// sink is disconnected.
    /// </para>
    /// </remarks>
#if NET_4_0 || MONO_4_0
        [System.Security.SecuritySafeCritical]
#endif
    public override void Shutdown()
    {
      // Stops the sink from receiving messages
      RemotingServices.Disconnect(m_sink);
      m_sink = null;

      base.Shutdown();
    }

    #endregion Override implementation of PluginSkeleton

    #region Private Instance Fields

    private RemoteLoggingSinkImpl m_sink;
    private string m_sinkUri;

    #endregion Private Instance Fields

    #region Private Static Fields

    /// <summary>
    /// The fully qualified type of the RemoteLoggingServerPlugin class.
    /// </summary>
    /// <remarks>
    /// Used by the internal logger to record the Type of the
    /// log message.
    /// </remarks>
    private static readonly Type declaringType = typeof(RemoteLoggingServerPlugin);

    #endregion Private Static Fields

    /// <summary>
    /// Delivers <see cref="LoggingEvent"/> objects to a remote sink.
    /// </summary>
    /// <remarks>
    /// <para>
    /// Internal class used to listen for logging events
    /// and deliver them to the local repository.
    /// </para>
    /// </remarks>
    private class RemoteLoggingSinkImpl : MarshalByRefObject, IRemoteLoggingSink
    {
      #region Public Instance Constructors

      /// <summary>
      /// Constructor
      /// </summary>
      /// <param name="repository">The repository to log to.</param>
      /// <remarks>
      /// <para>
      /// Initializes a new instance of the <see cref="RemoteLoggingSinkImpl"/> for the
      /// specified <see cref="ILoggerRepository"/>.
      /// </para>
      /// </remarks>
      public RemoteLoggingSinkImpl(ILoggerRepository repository)
      {
        m_repository = repository;
      }

      #endregion Public Instance Constructors

      #region Implementation of IRemoteLoggingSink

      /// <summary>
      /// Logs the events to the repository.
      /// </summary>
      /// <param name="events">The events to log.</param>
      /// <remarks>
      /// <para>
      /// The events passed are logged to the <see cref="ILoggerRepository"/>
      /// </para>
      /// </remarks>
      public void LogEvents(LoggingEvent[] events)
      {
        if (events != null)
        {
          foreach (LoggingEvent logEvent in events)
          {
            if (logEvent != null)
            {
              m_repository.Log(logEvent);
            }
          }
        }
      }

      #endregion Implementation of IRemoteLoggingSink

      #region Override implementation of MarshalByRefObject

      /// <summary>
      /// Obtains a lifetime service object to control the lifetime 
      /// policy for this instance.
      /// </summary>
      /// <returns><c>null</c> to indicate that this instance should live forever.</returns>
      /// <remarks>
      /// <para>
      /// Obtains a lifetime service object to control the lifetime 
      /// policy for this instance. This object should live forever
      /// therefore this implementation returns <c>null</c>.
      /// </para>
      /// </remarks>
#if NET_4_0 || MONO_4_0
            [System.Security.SecurityCritical]
#endif
      public override object InitializeLifetimeService()
      {
        return null;
      }

      #endregion Override implementation of MarshalByRefObject

      #region Private Instance Fields

      /// <summary>
      /// The underlying <see cref="ILoggerRepository" /> that events should
      /// be logged to.
      /// </summary>
      private readonly ILoggerRepository m_repository;

      #endregion Private Instance Fields
    }
  }
}

#endif // NET_2_0